#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>

using namespace std;

//************************************课堂
//进程等待是什么?
//让父进程fork后,需要通过wait/waitpid等待子进程的退出

//fork之后
//子进程:帮助父进程完成某种任务
//父进程:需要知道子进程的退出时的信息

//为什么要让父进程等待?
//1.通过获取子进程退出的信息,能够得知子进程执行结果
//2.可以保证:时序的问题,子进程先退出,父进程后退出
//3.进程退出的时候会先进入僵尸状态,会造成内存泄漏,需要通过父进程wait
//释放改子进程占用的资源

//另外,进程一旦变成僵尸状态,就是刀枪不入,kill -9 也是无能为力
//因为谁也没有办法杀死一个已经死去的进程

//*************************************课件
//wait方法
//#include<sys/types.h>
//#include<sys/wait.h>
//pid_t wait(int*status);
//返回值：
// 成功返回被等待进程pid，失败返回-1。
//参数：
// 输出型参数，获取子进程退出状态,不关心则可以设置成为NULL

//进程等待
//wait方法
//int main()
//{
//    int status;
//    pid_t id = fork();
//    pid_t ret_w;
//
//    if(id < 0)
//    {
//        cout << "create child process error:" << strerror(errno) << endl;
//    }
//    else if(id == 0)
//    {
//        cout << "i am child process, pid:" << getpid() << endl;
//        sleep(2);
//        exit(23);
//    }
//    else 
//    {
//        cout << "i am father process, ppid:" << getppid() << endl;
//        sleep(4);
//        ret_w = wait(&status);
//        if(ret_w > 0)
//        {
//            //WIFEXITED(status)这个宏是用来获取子进程退出是否正常
//            //WEXITSTATUS(status)这个宏是用来获取子进程的退出代码部分
//            cout << "wait seccess,child id:" << ret_w <<
//                " child exit code:" << WEXITSTATUS(status) <<
//                " child exit signal:" << WIFEXITED(status) << endl;
//        }
//        else 
//        {
//            perror("wait error:");
//        }
//    }
//
//}

//**********************************************课件
//pid_ t waitpid(pid_t pid, int *status, int options);
//返回值：
//当正常返回的时候waitpid返回收集到的子进程的进程ID；
//如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0；
//如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在；
//参数：
//pid：
//Pid=-1,等待任一个子进程。与wait等效。
//Pid>0.等待其进程ID与pid相等的子进程。
//status:
//WIFEXITED(status): 若为正常终止子进程返回的状态，则为真。（查看进程是否是正常退出）
//WEXITSTATUS(status): 若WIFEXITED非零，提取子进程退出码。（查看进程的退出码）
//WTERMSIG(status):获取子进程的结束信号
//options:
//WNOHANG: 若pid指定的子进程没有结束，则waitpid()函数返回0，不予以等待。若正常结束，则返回该子进程的ID。
//
//如果子进程已经退出，调用wait/waitpid时，wait/waitpid会立即返回，并且释放资源，获得子进程退
//出信息。
//如果在任意时刻调用wait/waitpid，子进程存在且正常运行，则进程可能阻塞。
//如果不存在该子进程，则立即出错返回。

//获取子进程status
//wait和waitpid，都有一个status参数，该参数是一个输出型参数，由操作系统填充。
//如果传递NULL，表示不关心子进程的退出状态信息。
//否则，操作系统会根据该参数，将子进程的退出信息反馈给父进程
//status不能简单的当作整形来看,我们只会研究它的低16位
//15~8:里面存储着子进程的退出代码(所以退出码不要太大,否则会溢出)
//0~6:里面存储着子进程的退出信号
//0~7:core dump标志

//*********************************************课堂
//bash是命令行启动的所有进程的父进程
//bash一定是通过wait方式得到子进程的退出结果,所以我们能够通过echo$?
//获取到子进程的退出码

//看到某些引用或者是OS本身卡住了长时间不动,说明应用或者程序hang住了
//所以WNOHANG的意思就是wait_no_hang

//阻塞是不是意味着父进程不被调度执行了?
//是的
//阻塞的时候,父进程的PCB被放入了等待队列,并将父进程的状态改为S状态
//当子进程执行完毕了,返回了,父进程的PCB从等待队列拿到了运行队列
//从而被CPU调度

//waitpid的阻塞等待
//int main()
//{
//    int status;
//    pid_t id = fork();
//    pid_t ret_w;
//
//    if(id < 0)
//    {
//        cout << "create child process error:" << strerror(errno) << endl;
//    }
//    else if(id == 0)
//    {
//        cout << "i am child process, pid:" << getpid() << endl;
//        sleep(4);
//        //int i = 2;
//        //i /= 0;
//        exit(23);
//    }
//    else 
//    {
//        cout << "i am father process, ppid:" << getppid() << endl;
//        sleep(2);
//        //ret_w = waitpid(id, &status, 0);//等待指定id的进程,状态为阻塞
//        ret_w = waitpid(-1, &status, 0);//等待任意子进程,状态为阻塞
//        if(ret_w > 0)
//        {
//            //WIFEXITED(status)这个宏是用来获取子进程的退出是否正常
//            //WEXITSTATUS(status)这个宏是用来获取子进程的退出代码部分
//            if(WIFEXITED(status))
//            {
//            cout << "wait seccess,child id:" << ret_w <<  
//                " child exit code:" << WEXITSTATUS(status) <<
//                " child exit status:" << WIFEXITED(status) << endl;
//            }
//            else 
//            {
//                cout << "wait seccess" <<
//                    " child process exit error" << endl;
//            }
//            
//        }
//        else 
//        {
//            perror("wait error");
//        }
//    }
//
//}

//基于非阻塞等待的轮询方案
int main()
{
    int status;
    pid_t id = fork();
    pid_t ret_w;

    if(id < 0)
    {
        cout << "create child process error:" << strerror(errno) << endl;
    }
    else if(id == 0)
    {
        cout << "i am child process, pid:" << getpid() << endl;
        sleep(1);
        exit(0);
    }
    else 
    {
        cout << "i am father process, ppid:" << getppid() << endl;

        //ret_w = waitpid(id, &status, 0);//等待指定id的进程,状态为阻塞
        //ret_w = waitpid(-1, &status, 0);//等待任意子进程,状态为阻塞
        while(1)
        {

            //子进程没有退出,但是waitpid等待是成功的,需要父进程重复进行等待
            ret_w = waitpid(id, &status, WNOHANG);//等待指定子进程,状态为非阻塞
            //这里父进程如果加上了sleep,那么结果没有什么问题
            //如果这里的sleep去掉了,就算子进程只是sleep(1),也会出现很长时间的高频打印wait continue
            //解释原因如下:打印数据太多,xshell来不及输出打印
            //数据都在网络上传输着,距离把全部数据打印出来需要好一段时间
            //所以我测试了几次,每次的时间都是不一样的
            //应该就是网络的稳定程度不一样
            
            sleep(1);
            if(ret_w > 0)
            {
                //WIFEXITED(status)这个宏是用来获取子进程的退出是否正常
                //WEXITSTATUS(status)这个宏是用来获取子进程的退出代码部分
                if(WIFEXITED(status))
                {
                    cout << "wait seccess,child id:" << ret_w <<  
                        " child exit code:" << WEXITSTATUS(status) <<
                        " child exit status:" << WIFEXITED(status) << endl;
                    break;
                }
                else 
                {
                    cout << "wait seccess" <<
                        " child process exit error" << 
                        " child exit signal:" <<WTERMSIG(status) << endl;
                    break;
                }
                
            }
            else if(ret_w == 0)
            {
                cout << "wait continue" << endl;
            }
            else 
            {
                perror("wait error");
                break;
            }
        }
    }

}
